package com.yanzhengwu.boot.core.aop;

import com.yanzhengwu.boot.core.common.annotation.DataSource;
import com.yanzhengwu.boot.core.datasource.DynamicDataSourceContextHolder;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;

@Slf4j
@Aspect
@Component
@Order(-1) // 保证该AOP在@Transactional之前执行
public class DynamicDataSourceAop {


    @Pointcut("@annotation(com.yanzhengwu.boot.core.common.annotation.DataSource)"
            + "|| @within(com.yanzhengwu.boot.core.common.annotation.DataSource)"
            //+ "|| execution(public * com..*.Maaper(..))"
    )
    //@Pointcut("execution(public * com..*(..))")
    public void dsPointCut() {
    }

    @Around("dsPointCut()")
    public Object around(ProceedingJoinPoint point) throws Throwable {
        Method targetMethod = this.getTargetMethod(point);
        DataSource dataSource = targetMethod.getAnnotation(DataSource.class);//获取要切换的数据源
        if (dataSource != null) {
            String slaveNodeName = dataSource.slaveNode().name();
            String otherSlaveNode = dataSource.otherSlaveNode();
            // TODO 注解的枚举常量转为小写 对应配置文件数据源
            DynamicDataSourceContextHolder.setDataSourceType(slaveNodeName.toLowerCase());
            // TODO 如果配置了其他数据源则 其他数据源优先级最高
            if (!"".equals(otherSlaveNode)) {
                DynamicDataSourceContextHolder.setDataSourceType(otherSlaveNode);
            }

        }
        try {
            return point.proceed();
        } finally {
            // TODO 销毁数据源 在执行方法之后 防止ThreadLocal 内存溢出
            DynamicDataSourceContextHolder.removeDataSourceType();
        }
    }

    /**
     * 获取目标方法
     */
    private Method getTargetMethod(ProceedingJoinPoint pjp) throws NoSuchMethodException {
        Signature signature = pjp.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method agentMethod = methodSignature.getMethod();
        DataSource annotation = agentMethod.getAnnotation(DataSource.class);
        //return pjp.getTarget().getClass().getMethod(agentMethod.getName(), agentMethod.getParameterTypes());
        return agentMethod;
    }
}
